

-- Namespace
	local name = 'nfCoreLib'
	local version = 0002
	
	local mod = _G[name]
	if mod then
		if mod.version >= version then
			return
		end
	else
		mod = {}
		_G[name] = mod
	end
	
	mod.version = version


-- New
	mod.__index = mod
	
	function mod:new(obj,name)
		if type(obj) ~= 'table' then
			obj = {}
		end
		if name then
			obj.name = name
		end
		setmetatable(obj,mod)
		return obj
	end


-- I/O
	function mod:print(msg,...)
		msg = tostring(msg)
		msg = '|cffff7fff%s|r: '..msg
		msg = msg:format((self.name or name),...)
		print(msg)
	end
	
	local function softerror(err)
		local h = geterrorhandler(err)
		if h then
			h(err)
		else
			err = tostring(err)
			err = '|cffff7f7fError|r: '..err
			print(err)
		end
	end
	mod.softerror = softerror


-- Events
	local events = mod.events
	local equeue = mod.equeue
	local eiters = mod.eiters
	local eframe = mod.eframe
	if not eframe then
		events = {}; mod.events = events
		equeue = {}; mod.equeue = equeue
		eiters = {}; mod.eiters = eiters
		eframe = CreateFrame('Frame'); mod.eframe = eframe
	end
	
	function mod:event_register(event,func)
		assert(type(event) == 'string','bad argument #1')
		assert(type(func) == 'function','bad argument #2')
		
		local etbl,eqt = events[event],equeue[event]
		if not etbl then
			etbl,eqt = {},{}
			events[event] = etbl
			equeue[event] = eqt
			eframe:RegisterEvent(event)
		end
		
		if not etbl[func] and not eqt[func] then
			if eiters[event] then
				eqt[func] = true
			else
				etbl[func] = true
			end
		end
		
		return func
	end
	
	function mod:event_unregister(event,func)
		local etbl,eqt = events[event],equeue[event]
		if not etbl then return end
		
		if not etbl[func] and not eqt[func] then
			return
		end
		
		etbl[func] = nil
		eqt[func] = nil
		
		if not next(etbl) and not next(eqt) then
			events[event] = nil
			equeue[event] = nil
			eframe:UnregisterEvent(event)
		end
	end
	
	local function call(_,event,...)
		if eiters[event] then
			mod:print('|cffff7f7fWarning:|r Event %s looped!',event)
			return
		end
		
		eiters[event] = true
		
		local etbl,eqt = events[event],equeue[event]
		
		local succ,err
		for func in pairs(etbl) do
			succ,err = pcall(func,...)
			if not succ then
				softerror(err)
			end
		end
		
		eiters[event] = nil
		
		if next(eqt) then
			for func in pairs(eqt) do
				etbl[func] = true
				eqt[func] = nil
			end
		end
	end
	
	function mod:event_trigger(event,...)
		if events[event] then
			call(nil,event,...)
		end
	end
	
	eframe:SetScript('OnEvent',call)


-- Timers
	local freq = (1/20)
	
	local timers = mod.timers
	local tqueue = mod.tqueue
	local tframe = mod.tframe
	if not tframe then
		timers = {}; mod.timers = timers
		tqueue = {}; mod.tqueue = tqueue
		tframe = CreateFrame('Frame')
		tframe.paused = true
		tframe.iterating = false
		tframe.throttle = 0
		tframe:Hide()
		mod.tframe = tframe
	end
	
	function mod:timer_register(delay,rpt,func)
		assert(type(delay) == 'number','bad argument #1')
		assert(type(func) == 'function','bad argument #3')
		
		local t = timers[func] or tqueue[func]
		if t then
			t.rpt = (not not rpt)
			t.max = delay
		else
			t = {
				min = 0,
				max = delay,
				rpt = (not not rpt),
			}
			if tframe.iterating then
				tqueue[func] = t
			else
				timers[func] = t
				if tframe.paused then
					tframe.paused = false
					tframe:Show()
				end
			end
		end
		
		return func
	end
	
	function mod:timer_unregister(func)
		local t = timers[func] or tqueue[func]
		if not t then return end
		
		timers[func] = nil
		tqueue[func] = nil
		
		if not next(timers) and not next(tqueue) then
			tframe.paused = true
			tframe.throttle = 0
			tframe:Hide()
		end
	end
	
	tframe:SetScript('OnUpdate',function(self,elapsed)
		self.iterating = true
		
		local throttle = self.throttle
		throttle = throttle + elapsed
		
		if throttle >= freq then
			local min,max,succ,err
			for func,t in pairs(timers) do
				min,max = t.min,t.max
				min = min + throttle
				if min >= max then
					succ,err = pcall(func,min)
					if not succ then
						softerror(err)
					end
					if t.rpt then
						t.min = 0
					else
						timers[func] = nil
					end
				else
					t.min = min
				end
			end
			
			self.throttle = 0
		else
			self.throttle = throttle
		end
		
		self.iterating = false
		
		if next(tqueue) then
			for func,t in pairs(tqueue) do
				timers[func] = t
				tqueue[func] = nil
			end
		end
		
		if not next(timers) then
			self.paused = true
			self.throttle = 0
			self:Hide()
		end
	end)


-- Anim timers
	-- Unthrottled timers.
	local anims = mod.anims
	local aqueue = mod.aqueue
	local aframe = mod.aframe
	if not aframe then
		anims = {}; mod.anims = anims
		aqueue = {}; mod.aqueue = aqueue
		aframe = CreateFrame('Frame')
		aframe.paused = true
		aframe.iterating = false
		aframe:Hide()
		mod.aframe = aframe
	end
	
	function mod:anim_register(func)
		assert(type(func) == 'function','bad argument #1')
		
		if not anims[func] and not aqueue[func] then
			if aframe.iterating then
				aqueue[func] = true
			else
				anims[func] = true
				if aframe.paused then
					aframe.paused = false
					aframe:Show()
				end
			end
		end
		
		return func
	end
	
	function mod:anim_unregister(func)
		if not anims[func] and not aqueue[func] then
			return
		end
		
		anims[func] = nil
		aqueue[func] = nil
		
		if not next(anims) and not next(aqueue) then
			aframe.paused = true
			aframe:Hide()
		end
	end
	
	aframe:SetScript('OnUpdate',function(self,elapsed)
		self.iterating = true
		
		local succ,err
		for func in pairs(anims) do
			succ,err = pcall(func,elapsed)
			if not succ then
				softerror(err)
			end
		end
		
		self.iterating = false
		
		if next(aqueue) then
			for func in pairs(aqueue) do
				anims[func] = true
				aqueue[func] = nil
			end
		end
		
		if not next(anims) then
			self.paused = true
			self:Hide()
		end
	end)


-- Frame management
	local function Listen(self,event,func)
		-- Insert into the highest-level events table.
		local f = self
		if f._nfparent then
			repeat
				f = f._nfparent
			until not f._nfparent
		end
		local events = f._nfevents
		
		local etbl = events[event]
		if not etbl then
			etbl = {}
			events[event] = etbl
		end
		etbl[#etbl+1] = { obj = self, func = func }
	end
	
	local function Update(self,event,...)
		local etbl = self._nfevents[event]
		if etbl then
			local e,succ,err
			for i = 1,#etbl do
				e = etbl[i]
				succ,err = pcall(e.func,e.obj,...)
				if not succ then
					softerror(err)
				end
			end
		end
	end
	
	local function HasUpdate(self,event)
		return (not not self._nfevents[event])
	end
	
	local function SetZLevel(self,lvl)
		local parent = self._nfparent
		if lvl <= 0 then
			self:SetParent(parent)
		else
			local zlevels = parent._nfzlevels
			if lvl > #zlevels then
				for i = #zlevels+1,lvl do
					zlevels[i] = CreateFrame('Frame',nil,(zlevels[i-1] or parent))
					zlevels[i]:SetAllPoints(true)
				end
			end
			self:SetParent(zlevels[lvl])
		end
	end
	
	local function CreateTexture(self,...)
		local r = self:_nfCreateTexture(...)
		r._nfparent = self
		r.Listen = Listen
		r.SetZLevel = SetZLevel
		return r
	end
	
	local function CreateFontString(self,...)
		local r = self:_nfCreateFontString(...)
		r._nfparent = self
		r.Listen = Listen
		r.SetZLevel = SetZLevel
		return r
	end
	
	local function CreateChild(self,ftype,name,inherits)
		local f = CreateFrame(ftype,name,self,inherits)
		f._nfparent = self
		f.Listen = Listen
		f.CreateChild = CreateChild
		return f
	end
	
	function mod:CreateFrame(...)
		local f = CreateFrame(...)
		
		f._nfevents = {}
		f.Listen = Listen
		f.Update = Update
		f.HasUpdate = HasUpdate
		
		f._nfzlevels = {}
		f._nfCreateTexture = f.CreateTexture
		f.CreateTexture = CreateTexture
		f._nfCreateFontString = f.CreateFontString
		f.CreateFontString = CreateFontString
		
		f.CreateChild = CreateChild
		
		return f
	end
